package com.zhaopin.qa.jmeter.plugins.multienvuser.controller;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.jmeter.config.Argument;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.control.LoopController;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.testelement.property.CollectionProperty;
import org.apache.jmeter.testelement.property.StringProperty;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;

import com.zhaopin.qa.jmeter.plugins.multienvuser.config.EnvArgument;
import com.zhaopin.qa.jmeter.plugins.multienvuser.config.EnvArgument.ArgumentsType;

/**
 * 根据用户数决定是否需要循环
 * 
 * @author wenke.dong
 *
 */
public class MultiEnvUserController extends LoopController implements Serializable {

	private static final long serialVersionUID = -1555977482845950843L;

	private static final Logger log = LoggerFactory.getLogger(MultiEnvUserControllerPanel.class);

	private static final String SELECTED_ENV = "multiEnvUser.selected_env";
	private static final String SELECTED_USERS = "multiEnvUser.selected_users";

	private static final String ENV_ARGS = "multiEnvUser.env_args";

	private static final String USER_ARGS = "multiEnvUser.env_user_args";

	/**
	 * 从系统参数里设置环境
	 */
	private static final String sysDefEnv = System.getProperty("env", "unset");

	/**
	 * 从系统参数里设置用户
	 */
	private static final String sysDefUsers = System.getProperty("users", "unset");// demo [user1,user2]

	private transient Map<String, Arguments> envArgsMap;

	private transient Map<String, Map<String, Arguments>> envUserArgsMap;

	/**
	 * 用户数
	 */
	private transient int userNumber;

	/**
	 * 执行环境
	 */
	private transient String env;

	/**
	 * 执行用户
	 */
	private transient List<String> users;

	/**
	 * 上一次设置的环境参数
	 */
	private transient final Arguments lastEnvArgs = new Arguments();

	/**
	 * 上一次设置的用户参数
	 */
	private transient final Arguments lastUserArgs = new Arguments();

	public MultiEnvUserController() {
		setProperty(new CollectionProperty(ENV_ARGS, new ArrayList<Arguments>()));
		setProperty(new CollectionProperty(USER_ARGS, new ArrayList<Arguments>()));
		setProperty(new CollectionProperty(SELECTED_USERS, new ArrayList<String>()));
	}

	@Override
	public Sampler next() {
		processVariables();
		return super.next();
	}

	/**
	 * 处理参数
	 */
	private void processVariables() {

		/*
		 * 所有循环首次进 取系统参数
		 */
		if (getIterCount() == 1 && isFirst()) {

			/*
			 * 进来先初始化成员变量
			 */
			initVariables();

			/*
			 * 环境参数只第一次进来的时候设置一次
			 */
			processEnvVariables();
		}

		if (userNumber >= Integer.valueOf(getLoopString())) {
			setLoops(userNumber);
		}
		// log("当前的循环：" + getIterCount() + "/" + getLoops() + " first:" + isFirst());

		/*
		 * 每个循环首次进 设置参数
		 */
		if (getIterCount() <= getLoops() && isFirst()) {
			processUserVariables();
		}

		/*
		 * 结束的时候把参数全清理了，避免gui模式出问题
		 */
		if (isDone()) {
			clearVariables();
		}
	}

	private void clearVariables() {
		env = null;
		users = null;
		envArgsMap = null;
		envUserArgsMap = null;
	}

	/**
	 * 把存起来的这个变量搞成成员变量
	 */
	private void initVariables() {
		log.info("设置本次执行的环境和用户变量！！");
		/*
		 * 要设置和环境的关联
		 */
		env = getSelectedEnv();
		users = getSelectedUser();

		{
			envArgsMap = new HashMap<>();
			envUserArgsMap = new HashMap<>();

			getEnvArgs().clone().forEach(envArgs1 -> {
				Arguments envArgs = (Arguments) envArgs1.getObjectValue();
				String envName = envArgs.getName().split("\\.")[2];// EnvUserArgs.env.qa

				envArgsMap.put(envName, (Arguments) envArgs.clone());
				envUserArgsMap.put(envName, new HashMap<>());
			});
		}

		{
			getUserArgs().clone().forEach(userArgs1 -> {
				Arguments userArgs = (Arguments) userArgs1.getObjectValue();
				String[] split = userArgs.getName().split("\\.");
				String envName = split[2];// EnvUserArgs.env.qa.user1
				String userName = split[3];// EnvUserArgs.env.qa.user2

				envUserArgsMap.get(envName).put(userName, (Arguments) userArgs.clone());
			});
		}

		setEnvAndUserFromSysProps();
		userNumber = users.size();
	}

	/**
	 * 处理全局参数 从外部设置</br>
	 * 从system参数设置环境 -Denv=""</br>
	 * 从system参数设置用户格式 -Dusers=[user1,user2]</br>
	 */
	private void setEnvAndUserFromSysProps() {
		log.info("检查系统设置...");

		if ("unset".equals(sysDefEnv)) {
			return;
		}
		if (envArgsMap.containsKey(sysDefEnv)) {
			log.info("使用系统设置执行环境:" + sysDefEnv);

			env = sysDefEnv;
			setLoops("1");
		} else {
			setLoops("0");
			log("系统执行环境设置有误，本次将不会执行！设置的环境是：" + sysDefEnv + "，可用环境列表是：" + envArgsMap.keySet(), Level.ERROR);
			return;
		}

		log.info("开始判断系统设置的执行用户...");
		log.info("系统用户设置 --> " + sysDefUsers);
		if ("unset".equals(sysDefUsers) || !(sysDefUsers.startsWith("[") && sysDefUsers.endsWith("]"))) {
			log("系统设置执行用户有误！！预期的格式设置是:[user1,user2]", Level.ERROR);
			return;
		}

		String[] sysDefUsersList = sysDefUsers.substring(1, sysDefUsers.length() - 1).split(",");

		/*
		 * 要设置和环境的关联
		 */
		Set<String> userNameList = envUserArgsMap.get(env).keySet();

		log.info("当前环境(" + env + ")可用用户列表 --> " + userNameList);

		List<String> validUserList = new ArrayList<>();

		Arrays.asList(sysDefUsersList).stream().forEach(user -> {
			if (userNameList.contains(user)) {
				validUserList.add(user);
			} else {
				log("系统执行用户设置有误，本次将不会执行！设置的用户是：" + user + "，" + getSelectedEnv() + "环境可用用户列表是：" + userNameList,
						Level.ERROR);
			}
		});
		log.info("使用系统设置用户: -->" + validUserList.toString());

		users.clear();
		users.addAll(validUserList);

		log.info("结束判断系统设置的执行用户。");
	}

	/**
	 * 处理当次选择的环境参数 重复设置未处理
	 */
	private void processEnvVariables() {
		/*
		 * 只有第一次设置环境参数
		 */
		if (!isFirst()) {
			return;
		}
		log("设置执行环境：" + env);

		Arguments envArgs = envArgsMap.get(env);

		resetLastUsedVariables(ArgumentsType.env, envArgs);
		// 设置环境
		JMeterUtils.getJMeterProperties().setProperty("env", env);

		putArgs2Vars(envArgs);

	}

	/**
	 * 处理循环中的用户参数
	 */
	private void processUserVariables() {
		log.info("检查未执行的用户:" + users);

		if (users.size() == 0) {
			log("并未设置用户！！原因：没有更多用户了!");
			return;
		}

		String user = users.remove(0);

		// 切换为 env 环境 user用户 执行。
		log("开始执行用户：" + user);

		Arguments userArg = envUserArgsMap.get(env).get(user);

		resetLastUsedVariables(ArgumentsType.user, userArg);

		userArg.addArgument(new EnvArgument("multiuser.current", user));

		putArgs2Vars(userArg);

	}

	/**
	 * 移除上一次设置的参数
	 */
	@SuppressWarnings("unchecked")
	private void resetLastUsedVariables(ArgumentsType argsType, Arguments currentEnvArgs) {
		final JMeterVariables vars = JMeterContextService.getContext().getVariables();

		switch (argsType) {
		case env:
			/*
			 * 移除旧的
			 */
			lastEnvArgs.forEach(arg -> {
				EnvArgument envArgument = (EnvArgument) arg.getObjectValue();

				if (envArgument.isGlobal()) {
					JMeterUtils.getJMeterProperties().remove(envArgument.getName());
				} else {
					vars.remove(envArgument.getName());
				}
			});
			/*
			 * 设置新的
			 */
			lastEnvArgs.removeAllArguments();

			break;
		case user:

			lastUserArgs.getArgumentsAsMap().keySet().stream().forEach(vars::remove);
			lastUserArgs.removeAllArguments();
			lastUserArgs.setArguments((ArrayList<Argument>) currentEnvArgs.getArguments().getObjectValue());
			break;
		default:
			return;
		}

	}

	/**
	 * 把参数放入vars中
	 * 
	 * @param args
	 */
	private void putArgs2Vars(Arguments args) {
		final Properties props = JMeterUtils.getJMeterProperties();
		final JMeterVariables vars = JMeterContextService.getContext().getVariables();

		args.forEach(arg1 -> {
			EnvArgument arg = (EnvArgument) arg1.getObjectValue();
			if (arg.isGlobal()) {
				props.setProperty(arg.getName(), arg.getValue());
			} else {
				vars.put(arg.getName(), arg.getValue());
			}
			log.info("Set " + (arg.isGlobal() ? "property:" : "variables:") + arg.getName() + "=" + arg.getValue());

		});

	}

	public void setSelectedEnv(String env) {
		log.info("选择环境：" + env);
		/*
		 * 选择环境后循环次数设置为1 每次只能执行一个环境
		 */
		setLoops("1");
		setProperty(new StringProperty(SELECTED_ENV, env));
	}

	public String getSelectedEnv() {
		return ((StringProperty) getProperty(SELECTED_ENV)).getStringValue();
	}

	public void setSelectedUser(List<String> arrayList) {
		log("设置执行用户：" + arrayList);
		/*
		 * 选择用户后 将循环次数设置为用户数
		 */
		// 选择了一个环境就有一次循环，如果选择的用户的循环次数大于已有的循环那么设置 否则不设置
		if (arrayList.size() > Integer.valueOf(getLoopString())) {
			setLoops(arrayList.size());
		}
		setProperty(new CollectionProperty(SELECTED_USERS, arrayList));
	}

	public List<String> getSelectedUser() {
		List<String> userList = new ArrayList<>();

		((CollectionProperty) getProperty(SELECTED_USERS)).forEach((user) -> {
			userList.add(user.getStringValue());
		});

		return userList;
	}

	public void setEnvArgs(List<Arguments> envArgs) {
		setProperty(new CollectionProperty(ENV_ARGS, envArgs));
	}

	public CollectionProperty getEnvArgs() {
		return (CollectionProperty) getProperty(ENV_ARGS);
	}

	public void setUserArgs(List<Arguments> userArgs) {
		setProperty(new CollectionProperty(USER_ARGS, userArgs));
	}

	public CollectionProperty getUserArgs() {
		return (CollectionProperty) getProperty(USER_ARGS);
	}

	private void log(String str) {
		log(str, Level.INFO);
	}

	private void log(String str, Level level) {
		switch (level) {
		case ERROR:
			log.error(str);
			System.err.println(str);
			break;
		default:
			log.info(str);
			System.out.println(str);
			break;
		}

	}
}
